/**
 * @section LICENSE
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details at
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <BlockDropperGame.h>

#include <iostream>

namespace BD = BlockDropper;

BD::BlockDropperGame::BlockDropperGame()
	: memberFreeBlock()	
	, savedActions()
{
}

double BD::BlockDropperGame::clearerPos() const
{
	return memberClearerPos;
}

const BD::FreeBlock & BD::BlockDropperGame::freeBlock() const
{
	return memberFreeBlock;
}

const boost::optional<BD::CellState> & BD::BlockDropperGame::getBoardItem(const Point<unsigned short> & point) const
{
	return board[point.y][point.x];
}

void BD::BlockDropperGame::runOneFrame(const double & time, const BlockDropperActions & actions)
{
	if(timeOfLastMove == 0.0)
	{
		timeOfLastMove = time;
	}

	if
	(
		time - timeOfLastMove > 0.25
		||
		actions.find(ACTION_MOVE_DOWN) != actions.end()
	)
	{
		const Point<unsigned short> & position(memberFreeBlock.position());

		if
		(
			position.y == height - 2
			||
			board[position.y + 2][position.x]
			||
			board[position.y + 2][position.x + 1]
		)
		{
			board[position.y    ][position.x    ] = CellState(memberFreeBlock.getConstituentBlock(0, 0));
			board[position.y    ][position.x + 1] = CellState(memberFreeBlock.getConstituentBlock(1, 0));
			board[position.y + 1][position.x    ] = CellState(memberFreeBlock.getConstituentBlock(0, 1));
			board[position.y + 1][position.x + 1] = CellState(memberFreeBlock.getConstituentBlock(1, 1));
			memberFreeBlock = FreeBlock();
		}
		else
		{
			memberFreeBlock.drop();
		}

		if(time - timeOfLastMove > 0.25)
		{
			timeOfLastMove += 0.25;
		}
	}

	{
		const Point<unsigned short> & position(memberFreeBlock.position());

		if
		(
			actions.find(ACTION_MOVE_LEFT) != actions.end()
			&&
			(
				!savedActions.getAction(ACTION_MOVE_LEFT)
				||
				time - *savedActions.getAction(ACTION_MOVE_LEFT) > 0.075
			)
			&&
			position.x > 0
			&&
			!board[position.y    ][position.x - 1]
			&&
			!board[position.y + 1][position.x - 1]
		)
		{
			memberFreeBlock.moveLeft();
			savedActions.updateAction(ACTION_MOVE_LEFT, time);
		}
	}

	{
		const Point<unsigned short> & position(memberFreeBlock.position());

		if
		(
			actions.find(ACTION_MOVE_RIGHT) != actions.end()
			&&
			(
				!savedActions.getAction(ACTION_MOVE_RIGHT)
				||
				time - *savedActions.getAction(ACTION_MOVE_RIGHT) > 0.075
			)
			&&
			position.x < width - 2
			&&
			!board[position.y    ][position.x + 2]
			&&
			!board[position.y + 1][position.x + 2]
		)
		{
			memberFreeBlock.moveRight();
			savedActions.updateAction(ACTION_MOVE_RIGHT, time);
		}
	}

	if
	(
		actions.find(ACTION_TURN_CLOCKWISE) != actions.end()
		&&
		(
			!savedActions.getAction(ACTION_TURN_CLOCKWISE)
			||
			time - *savedActions.getAction(ACTION_TURN_CLOCKWISE) > 0.20
		)
	)
	{
		memberFreeBlock.turnClockwise();
		savedActions.updateAction(ACTION_TURN_CLOCKWISE, time);
	}

	if
	(
		actions.find(ACTION_TURN_ANTICLOCKWISE) != actions.end()
		&&
		(
			!savedActions.getAction(ACTION_TURN_ANTICLOCKWISE)
			||
			time - *savedActions.getAction(ACTION_TURN_ANTICLOCKWISE) > 0.20
		)
	)
	{
		memberFreeBlock.turnAntiClockwise();
		savedActions.updateAction(ACTION_TURN_ANTICLOCKWISE, time);
	}

	for(unsigned short y(0); y < height - 1; ++y)
	{
		for(unsigned short x(0); x < width; ++x)
		{
			if(board[y][x] && !board[y + 1][x])
			{
				std::swap(board[y][x], board[y + 1][x]);
			}
		}
	}

	for(unsigned short y(0); y < height - 1; ++y)
	{
		for(unsigned short x(0); x < width - 1; ++x)
		{
			if
			(
				board[y][x]
				&&
				board[y][x + 1]
				&&
				board[y + 1][x]
				&&
				board[y + 1][x + 1]
				&&
				board[y][x]->value == board[y][x + 1]->value
				&&
				board[y][x + 1]->value == board[y + 1][x]->value
				&&
				board[y + 1][x]->value == board[y + 1][x + 1]->value
			)
			{
				board[y    ][x    ]->markDeletion = true;
				board[y    ][x + 1]->markDeletion = true;
				board[y + 1][x    ]->markDeletion = true;
				board[y + 1][x + 1]->markDeletion = true;
			}
		}
	}

	const unsigned short oldPos(std::floor(memberClearerPos));

	memberClearerPos += 2.0 / 60.0;

	while(memberClearerPos >= width)
	{
		memberClearerPos -= width;
	}

	const unsigned short newPos(std::floor(memberClearerPos));

	if(oldPos != newPos)
	{
		for(unsigned short y(0); y < height; ++y)
		{
			if(board[y][oldPos] && board[y][oldPos]->markDeletion)
			{
				board[y][oldPos].reset();
			}
		}
	}
}

